package com.atzly.community.utils;

import com.atzly.community.datasource.bean.IGetter;
import com.atzly.community.datasource.bean.ISetter;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.cglib.beans.BeanCopier;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;

/**
 * 
 * @author wangwei13@huice.com
 *
 */
public class ReflectUtils {

	private static Logger LOG = LoggerFactory.getLogger(ReflectUtils.class);
	
	public static Map<String,BeanCopier> beanCopierMap = new HashMap<String,BeanCopier>();

	/**
	 * 获取对象的属性值，待优化
	 * 
	 * @param o
	 * @param propName
	 * @return
	 * @throws NoSuchFieldException
	 * @throws SecurityException
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 */
	@SuppressWarnings("rawtypes")
	public static Object getProperty(Object o, String propName)  {
		if (o == null)
			return null;

		String curPropName = propName;
		String subPropName = "";
		int nestCharIdx = curPropName.indexOf(".");
		// 含有下级属性时，需要设置当前属性名，及下级属性名
		if (nestCharIdx >= 0) {
			curPropName = propName.substring(0, nestCharIdx);
			if (nestCharIdx != (propName.length() - 1)) {
				subPropName = propName.substring(nestCharIdx + 1);
			}
		}
		Object curPropObject = null;
		// 获取当前属性的对象
		if (o instanceof Map) {
			curPropObject = ((Map) o).get(curPropName);
		}
		else if(o instanceof IGetter){
			curPropObject = ((IGetter)o).get(curPropName);
		}
		else {
			BeanWrapper beanWrapper = new BeanWrapperImpl(o);
			curPropObject = beanWrapper.getPropertyValue(curPropName);
		}
		// 如果下级属性不为空，则递归调用getProperty
		if (!StringUtils.isBlank(subPropName)) {
			if(curPropObject==null) return null;
			
			return getProperty(curPropObject, subPropName);
		} else {
			// 如果下级属性为空，则返回当前属性对象
			return curPropObject;
		}
	}

	/**
	 * 设置对象属性值
	 * 
	 * @param o
	 * @param propName
	 * @param value
	 * @throws NoSuchFieldException
	 * @throws SecurityException
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static void setProperty(Object o, String propName, Object value)  {
		
		if(o == null) return;
		
		String curPropName = propName;
		String subPropName = "";
		int nestCharIdx = curPropName.indexOf(".");
		// 含有下级属性时，需要设置当前属性名，及下级属性名
		if (nestCharIdx >= 0) {
			curPropName = propName.substring(0, nestCharIdx);
			if (nestCharIdx != (propName.length() - 1)) {
				subPropName = propName.substring(nestCharIdx + 1);
			}
		}
		// 含有下级属性时
		if (!StringUtils.isBlank(subPropName)) {
			// 获取当前属性对象，用来递归调用
			Object curPropObject = getProperty(o,curPropName);
			
			setProperty(curPropObject, subPropName, value);
		} else {
			// 不含有下级属性时，直接设置属性值
			if (o instanceof Map) {
				((Map) o).put(curPropName, value);
			} 
			else if(o instanceof ISetter) {
				((ISetter)o).set(curPropName,value);
			}
			else {
				BeanWrapper beanWrapper = new BeanWrapperImpl(o);
				beanWrapper.setPropertyValue(curPropName, value);
			}
		}
	}

	// add later
	public static boolean hasProperty(Object o, String propName) {
		return true;
	}
	
	public static <T> T mapToBean(Map<String,Object> vars,Class<T> clazz) {
		T entity = ReflectUtils.classToObject(clazz);
		
		setProperties(entity,vars);
		
		return entity;
	}
	
	public static <T> void setProperties(T entity,Map<String,Object> vars) {
		if(vars == null)
			return;
		
		try {
			BeanInfo beanInfo = Introspector.getBeanInfo(entity.getClass());
			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
			for (PropertyDescriptor property : propertyDescriptors) {
				String key = property.getName();

				if (vars.containsKey(key)) {
					Object value = vars.get(key);
					// 得到property对应的setter方法
					Method setter = property.getWriteMethod();
					if(setter != null){
						try{
							setter.invoke(entity, value);
						}catch(Exception e) {
							LOG.error(" copy property error,property name {},target {},{}",key,entity,e);
						}
					}	
				}
			}
		} catch (Exception e) {
			LOG.error(" copy properties error,source {},target {},{}",vars,entity,e);
		}
	}
	
	public static void copyProperties(Object source, Object target){
        String beanKey =  generateKey(source.getClass(), target.getClass());
        BeanCopier copier =  null;
        if(!beanCopierMap.containsKey(beanKey)){
             copier = BeanCopier.create(source.getClass(), target.getClass(), false);
             beanCopierMap.put(beanKey, copier);
        }else{
             copier = beanCopierMap.get(beanKey);
        }
        copier.copy(source, target, null);
    }
	
    private static String generateKey(Class<?> class1,Class<?>class2){
        return class1.toString() + class2.toString();
    }

	public static Class<?> classForName(String name) {
		try {
			Class<?> cls = Class.forName(name);

			return cls;

		} catch (ClassNotFoundException e) {

			LOG.trace("lang.err_load_class", e);

			try {
				ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

				return Class.forName(name, true, classLoader);

			} catch (Exception t) {
				throw new RuntimeException("lang.err_load_class_fail", t);
			}
		}
	}

	public static Constructor<?> getConstructor(Class<?> clazz, Object arg) {

		Constructor<?>[] constructors = clazz.getConstructors();

		int n = constructors.length;
		for (int i = 0; i < n; i++) {
			Constructor<?> constructor = constructors[i];
			Class<?>[] types = constructor.getParameterTypes();
			if (types.length == 1) {

				if (arg == null)
					return constructor;

				if (types[0].isInstance(arg))
					return constructor;
			}
		}
		return null;
	}

	public static Object classToObject(Class<?> clazz, Object arg) throws Exception {

		Constructor<?> constructor = getConstructor(clazz, arg);
		if (constructor == null)
			throw new RuntimeException("java.err_invalid_proper_constructor");

		return constructor.newInstance(new Object[] { arg });
	}

	public static <T> T classToObject(Class<T> clazz) {
		T ret = null;
		try {
			ret =  clazz.newInstance();
		} catch (Exception e) {
			throw new RuntimeException("java.CAN_fail_new_object");
		}

		return ret;
	}
	
	public static Method safeGetDeclaredMethod(Class<?> clazz,String methodName,Class<?>... parameterTypes) {
		Method method = null;
		
		try{
			method = clazz.getDeclaredMethod(methodName, parameterTypes);
		}catch(Exception e) {
			LOG.error("{} not found in {}", methodName, clazz);
		}
		
		return method;
	}
	
	public static Method safeGetMethod(Class<?> clazz,String methodName,Class<?>... parameterTypes) {
		Method method = null;
		
		try{
			method = clazz.getMethod(methodName, parameterTypes);
		}catch(Exception e) {
			LOG.error("{} not found in {}", methodName, clazz);
		}
		
		return method;
	}
	
	public static Field safeGetField(Class<?> clazz,String fieldName) {
		Field field = null;
		
		try {
			field = clazz.getField(fieldName);
		}catch(Exception e) {
			LOG.error("{} not found in {}", fieldName, clazz);
		}
		
		return field;
	}
	
	public static Class<?> getClassFirstParameterizedType(Class<?> clazz) {
		Type[] interfacesTypes = clazz.getGenericInterfaces();
        for (Type t : interfacesTypes) {
        	if( t instanceof ParameterizedType) {
        		Type[] genericType = ((ParameterizedType) t).getActualTypeArguments();
        		return (Class<?>)genericType[0];
        	}
        }
        return null;
	}
	
}
